package com.source3g.dubbo.config.spring;

import com.alibaba.dubbo.config.annotation.Service;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.type.filter.AnnotationTypeFilter;

import java.util.Collections;
import java.util.Set;

/**
 * Created by jianglin on 2016/12/28
 */
public class DubboServiceBeanPostProcessor implements BeanFactoryPostProcessor, BeanPostProcessor, ApplicationContextAware {
    protected final Log logger = LogFactory.getLog(getClass());
    private       ApplicationContext applicationContext;
    private final Set<String>        packagesToScan;

    public DubboServiceBeanPostProcessor(Set<String> packagesToScan) {
        this.packagesToScan = packagesToScan;
    }

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        registerServiceBean(bean, beanName);
        return bean;
    }

    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        ClassPathBeanDefinitionScanner scanner = createBeanDefinitionScanner((BeanDefinitionRegistry) beanFactory);
        for (String packageToScan : this.packagesToScan) {
            scanner.scan(packageToScan);
        }
    }

    private void registerServiceBean(Object bean, String beanName) throws BeansException {
        Service service = AnnotatedElementUtils.getMergedAnnotation(bean.getClass(), Service.class);
        if (service == null) {
            return;
        }
        ServiceBeanFactory<Object> serviceBeanFactory = new ServiceBeanFactory<Object>(service);
        serviceBeanFactory.setApplicationContext(this.applicationContext);
        serviceBeanFactory.setBeanName(beanName);
        serviceBeanFactory.setInterface(bean.getClass().getInterfaces()[0]);
        serviceBeanFactory.setRef(bean);
        try {
            serviceBeanFactory.afterPropertiesSet();
        } catch (Exception e) {
            logger.error("", e);
            throw new BeanInitializationException("", e);
        }
        serviceBeanFactory.export();
    }

    private ClassPathBeanDefinitionScanner createBeanDefinitionScanner(BeanDefinitionRegistry registry) {
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(
                registry);
        scanner.setEnvironment(this.applicationContext.getEnvironment());
        scanner.setResourceLoader(this.applicationContext);
        scanner.addIncludeFilter(new AnnotationTypeFilter(
                Service.class));
        return scanner;
    }

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public Set<String> getPackagesToScan() {
        return Collections.unmodifiableSet(this.packagesToScan);
    }
}
